import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
path = r"C:\Users\NOGO G$\Desktop\ISJ\ISJ_perso\Projet\Global_fashion_Analysis\Fashion_data\ventes.csv"
df = pd.read_csv (path)
df.shape
(6416827, 19)
# mise en forme des noms de colonnes
df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
df.columns
Index(['invoice_id', 'line', 'customer_id', 'product_id', 'size', 'color',
'unit_price', 'quantity', 'date', 'discount', 'line_total', 'store_id',
'employee_id', 'currency', 'currency_symbol', 'sku', 'transaction_type',
'payment_method', 'invoice_total'],
dtype='object')
colonnes_utiles = [
'invoice_id', 'customer_id', 'date', 'unit_price', 'quantity',
'discount', 'line_total', 'invoice_total', 'payment_method'
]
df = df[colonnes_utiles]
"""
Génération des âges avec une distribution normale, centrée autour de 42 ans et
écart-type de 10 pour avoir une distribution réaliste
"""
df['age'] = np.random.normal(loc=42, scale=10, size=df.shape[0])
df['age'] = df['age'].clip(18, 75).astype(int)
# Création des catégories (bins)
bins = [17, 25, 35, 45, 55, 65, 100]
labels = ['18-25 ans', '26-35 ans', '36-45 ans', '46-55 ans', '56-65 ans', '+66 ans']
df['age_bin'] = pd.cut(df['age'], bins=bins, labels=labels)
df.head()
| invoice_id | customer_id | date | unit_price | quantity | discount | line_total | invoice_total | payment_method | age | age_bin | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | INV-US-001-03558761 | 47162 | 2023-01-01 15:42:00 | 80.5 | 1 | 0.0 | 80.5 | 126.7 | Cash | 47 | 46-55 ans |
| 1 | INV-US-001-03558761 | 47162 | 2023-01-01 15:42:00 | 31.5 | 1 | 0.4 | 18.9 | 126.7 | Cash | 47 | 46-55 ans |
| 2 | INV-US-001-03558761 | 47162 | 2023-01-01 15:42:00 | 45.5 | 1 | 0.4 | 27.3 | 126.7 | Cash | 20 | 18-25 ans |
| 3 | INV-US-001-03558762 | 10142 | 2023-01-01 20:04:00 | 70.0 | 1 | 0.4 | 42.0 | 77.0 | Cash | 63 | 56-65 ans |
| 4 | INV-US-001-03558762 | 10142 | 2023-01-01 20:04:00 | 26.0 | 1 | 0.0 | 26.0 | 77.0 | Cash | 50 | 46-55 ans |
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 6416827 entries, 0 to 6416826 Data columns (total 11 columns): # Column Dtype --- ------ ----- 0 invoice_id object 1 customer_id int64 2 date object 3 unit_price float64 4 quantity int64 5 discount float64 6 line_total float64 7 invoice_total float64 8 payment_method object 9 age int64 10 age_bin category dtypes: category(1), float64(4), int64(3), object(3) memory usage: 495.7+ MB
#Petit cadeau : 2 alternatives au describe pour les statistiques des colonnes 😁👌
from skimpy import skim
#from summarytools import dfSummary
from IPython.core.display import display, HTML
print("Résumé rapide avec skimpy :")
skim(df)
Résumé rapide avec skimpy :
╭──────────────────────────────────────────────── skimpy summary ─────────────────────────────────────────────────╮ │ Data Summary Data Types Categories │ │ ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓ ┏━━━━━━━━━━━━━┳━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━┓ │ │ ┃ Dataframe ┃ Values ┃ ┃ Column Type ┃ Count ┃ ┃ Categorical Variables ┃ │ │ ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩ ┡━━━━━━━━━━━━━╇━━━━━━━┩ ┡━━━━━━━━━━━━━━━━━━━━━━━┩ │ │ │ Number of rows │ 6416827 │ │ float64 │ 4 │ │ age_bin │ │ │ │ Number of columns │ 11 │ │ string │ 3 │ └───────────────────────┘ │ │ └───────────────────┴─────────┘ │ int64 │ 3 │ │ │ │ category │ 1 │ │ │ └─────────────┴───────┘ │ │ number │ │ ┏━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓ │ │ ┃ column ┃ NA ┃ NA % ┃ mean ┃ sd ┃ p0 ┃ p25 ┃ p50 ┃ p75 ┃ p100 ┃ hist ┃ │ │ ┡━━━━━━━━━━━━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩ │ │ │ customer_id │ 0 │ 0 │ 732600 │ 487500 │ 1 │ 294500 │ 676500 │ 1139000 │ 1643000 │ ▇▆▆▅▅▅ │ │ │ │ unit_price │ 0 │ 0 │ 132.5 │ 185.1 │ 2 │ 32.5 │ 51 │ 116.5 │ 1154 │ ▇▁▁ │ │ │ │ quantity │ 0 │ 0 │ 1.1 │ 0.3964 │ 1 │ 1 │ 1 │ 1 │ 3 │ ▇ │ │ │ │ discount │ 0 │ 0 │ 0.1191 │ 0.199 │ 0 │ 0 │ 0 │ 0.25 │ 0.6 │ ▇ ▁▂ │ │ │ │ line_total │ 0 │ 0 │ 114.2 │ 211.6 │ -3348 │ 24.75 │ 43.5 │ 109 │ 3460 │ ▇▅ │ │ │ │ invoice_total │ 0 │ 0 │ 243.5 │ 536.7 │ -6750 │ 34.02 │ 83.5 │ 241 │ 8977 │ ▇ │ │ │ │ age │ 0 │ 0 │ 41.53 │ 9.917 │ 18 │ 35 │ 41 │ 48 │ 75 │ ▂▅▇▅▂ │ │ │ └─────────────────┴─────┴───────┴─────────┴─────────┴────────┴─────────┴────────┴─────────┴─────────┴────────┘ │ │ category │ │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓ │ │ ┃ column ┃ NA ┃ NA % ┃ ordered ┃ unique ┃ │ │ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩ │ │ │ age_bin │ 0 │ 0 │ True │ 6 │ │ │ └──────────────────────────┴────────────┴──────────────────┴──────────────────────────┴──────────────────────┘ │ │ string │ │ ┏━━━━━━━━━━━┳━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┓ │ │ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ chars per ┃ words per ┃ total ┃ │ │ ┃ column ┃ NA ┃ NA % ┃ shortest ┃ longest ┃ min ┃ max ┃ row ┃ row ┃ words ┃ │ │ ┡━━━━━━━━━━━╇━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ │ │ invoice_i │ 0 │ 0 │ INV-US-00 │ INV-US-00 │ INV-CN-00 │ RET-US-00 │ 19 │ 1 │ 6416827 │ │ │ │ d │ │ │ 1-0355876 │ 1-0355876 │ 6-0286499 │ 5-0436404 │ │ │ │ │ │ │ │ │ │ 1 │ 1 │ 7 │ 3 │ │ │ │ │ │ │ date │ 0 │ 0 │ 2023-01-0 │ 2023-01-0 │ 2023-01-0 │ 2025-03-1 │ 19 │ 2 │ 12833654 │ │ │ │ │ │ │ 1 │ 1 │ 1 │ 8 │ │ │ │ │ │ │ │ │ │ 15:42:00 │ 15:42:00 │ 00:00:00 │ 20:59:00 │ │ │ │ │ │ │ payment_m │ 0 │ 0 │ Cash │ Credit │ Cash │ Credit │ 9.6 │ 1.8 │ 11552125 │ │ │ │ ethod │ │ │ │ Card │ │ Card │ │ │ │ │ │ └───────────┴────┴──────┴───────────┴───────────┴───────────┴───────────┴───────────┴───────────┴────────────┘ │ ╰────────────────────────────────────────────────────── End ──────────────────────────────────────────────────────╯
print( "Nous avons {} clients dans notre base".format(df["customer_id"].nunique()))
Nous avons 1283707 clients dans notre base
#Vérification des valeurs manquantes
df.isnull().sum()
invoice_id 0 customer_id 0 date 0 unit_price 0 quantity 0 discount 0 line_total 0 invoice_total 0 payment_method 0 age 0 age_bin 0 dtype: int64
# Aggrégation du nombre de clients et du chiffre d'afffaire par tranches d'age
age_stats = df.groupby('age_bin').agg({
'customer_id': pd.Series.nunique,
'invoice_total': 'sum'
}).reset_index().rename(columns={
'customer_id': 'nb_clients',
'invoice_total': 'total_ventes'
})
age_stats
| age_bin | nb_clients | total_ventes | |
|---|---|---|---|
| 0 | 18-25 ans | 287995 | 8.556600e+07 |
| 1 | 26-35 ans | 748610 | 3.429937e+08 |
| 2 | 36-45 ans | 967061 | 5.950279e+08 |
| 3 | 46-55 ans | 822339 | 4.124883e+08 |
| 4 | 56-65 ans | 360588 | 1.136672e+08 |
| 5 | +66 ans | 50952 | 1.292532e+07 |
import plotly.io as pio
pio.templates.default = "plotly_dark"
fig_clients = px.bar(
age_stats,
x='age_bin',
y='nb_clients',
title='Nombre de clients uniques par tranche d’âge :',
labels={'nb_clients': 'Nombre de clients', 'age_bin': 'Tranche d’âge'},
text='nb_clients'
)
pip install -U kaleido
Defaulting to user installation because normal site-packages is not writeable Requirement already up-to-date: kaleido in c:\users\nogo g$\appdata\roaming\python\python39\site-packages (0.2.1) Note: you may need to restart the kernel to use updated packages.
WARNING: You are using pip version 20.2.3; however, version 25.1.1 is available. You should consider upgrading via the 'c:\program files\python39\python.exe -m pip install --upgrade pip' command.
fig_clients.update_traces(textposition='outside')
fig_clients.write_image("clients.png")
fig_clients.show()
fig_ventes = px.bar(
age_stats,
x='age_bin',
y='total_ventes',
title='CA par tranche d’âge :',
labels={'total_ventes': 'Montant total (€)', 'age_bin': 'Tranche d’âge'},
text='total_ventes'
)
fig_ventes.update_traces(texttemplate='%{text:.2s} €', textposition='outside')
fig_ventes.show()
fig_ventes.write_image("fig_ventes.png")
# Répartition par moyen de payement
payement_stats = df.groupby('payment_method').agg({
'customer_id': pd.Series.nunique, # nombre de clients distincts
'invoice_total': 'sum' # total des ventes
}).reset_index().rename(columns={
'customer_id': 'nb_clients',
'invoice_total': 'montant_Total'
})
payement_stats
| payment_method | nb_clients | montant_Total | |
|---|---|---|---|
| 0 | Cash | 597270 | 3.108976e+08 |
| 1 | Credit Card | 1203441 | 1.251771e+09 |
from plotly.subplots import make_subplots
payement_method_pie_1 = px.pie(
payement_stats,
names='payment_method',
values='montant_Total',
title='Répartition des montants par méthode de payement',
labels={'montant_Total': 'Montant (€)', 'payment_method': 'méthtode de payement'},
)
payement_method_pie_2 = px.pie(
payement_stats,
names='payment_method',
values='nb_clients',
title='Répartition des clients par méthode de payement',
labels={'nb_clients': 'Montant (€)', 'payment_method': 'méthtode de payement'},
)
# Créer un subplot avec 1 ligne et 2 colonnes
fig = make_subplots(rows=1, cols=2, subplot_titles=('Nombre de clients', 'Montant (€)'),
specs=[[{'type': 'pie'}, {'type': 'pie'}]])
# Ajouter les graphiques en secteurs dans le subplot
fig.add_trace(payement_method_pie_2.data[0], row=1, col=1)
fig.add_trace(payement_method_pie_1.data[0], row=1, col=2)
# Mettre à jour le layout pour afficher correctement les titres et ajuster le style
fig.update_layout(
title_text="Répartition du nombre de clients et CA par méthode de payemennt :",
showlegend=True
)
# Afficher le graphique
fig.show()
fig.write_image("payement_method.png")
#Traitement de la date
df['date'] = pd.to_datetime(df['date'], errors='coerce')
df = df.dropna(subset=['date'])
# Date de référence pour le calcul de la récence R
ref_date = df['date'].max() + pd.Timedelta(days=1)
#Création de notre table client pour le scoring
rfm = df.groupby('customer_id').agg({
'date': lambda x: (ref_date - x.max()).days,
'invoice_id': 'nunique',
'invoice_total': 'sum'
}).reset_index()
rfm.columns = ['customer_id', 'Recency', 'Frequency', 'Monetary']
rfm.head()
| customer_id | Recency | Frequency | Monetary | |
|---|---|---|---|---|
| 0 | 1 | 102 | 5 | 831.30 |
| 1 | 2 | 34 | 4 | 1840.77 |
| 2 | 3 | 297 | 3 | 136.50 |
| 3 | 4 | 527 | 2 | 349.70 |
| 4 | 5 | 112 | 11 | 2273.00 |
rfm.to_csv("rfm_nextstep_export.csv", index=False, encoding='utf-8-sig')